package service

import (
	"context"
	"fmt"
	"time"

	ticketv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/ticket/v1"
	"github.com/devops-smartbot/devops-smartbot/services/ticket-service/internal/model"
	"github.com/devops-smartbot/devops-smartbot/services/ticket-service/internal/repository"
	"github.com/google/uuid"
)

// Service exposes ticket business operations.
//go:generate mockgen -destination=../mocks/service.go -package=mocks . Service
type Service interface {
	ListTickets(ctx context.Context, req *ticketv1.ListTicketsRequest) ([]model.Ticket, int64, error)
	GetTicket(ctx context.Context, id string) (*model.Ticket, error)
	CreateTicket(ctx context.Context, req *ticketv1.CreateTicketRequest) (*model.Ticket, error)
	GetTicketStatistics(ctx context.Context) (*ticketv1.GetTicketStatisticsResponse, error)
	UpdateTicket(ctx context.Context, req *ticketv1.UpdateTicketRequest) (*model.Ticket, error)
	AddComment(ctx context.Context, ticketID string, content string, userName string, isInternal bool) (*model.Ticket, error)
}

// ticketService implements Service using a repository.
type ticketService struct {
	repo repository.Repository
}

// NewTicketService creates a new service instance.
func NewTicketService(repo repository.Repository) Service {
	return &ticketService{repo: repo}
}

func (s *ticketService) ListTickets(ctx context.Context, req *ticketv1.ListTicketsRequest) ([]model.Ticket, int64, error) {
	pagination := req.GetPagination()
	page := pagination.GetPage()
	if page <= 0 {
		page = 1
	}
	pageSize := pagination.GetPageSize()
	if pageSize <= 0 {
		pageSize = 20
	}

	filter := repository.Filter{
		Status:   req.GetStatus(),
		Priority: req.GetPriority(),
		Limit:    int64(pageSize),
		Offset:   int64((page - 1) * pageSize),
	}

	return s.repo.ListTickets(ctx, filter)
}

func (s *ticketService) GetTicket(ctx context.Context, id string) (*model.Ticket, error) {
	return s.repo.GetTicket(ctx, id)
}

func (s *ticketService) CreateTicket(ctx context.Context, req *ticketv1.CreateTicketRequest) (*model.Ticket, error) {
	ticket := &model.Ticket{
		ID:           uuid.New().String(),
		TicketNumber: fmt.Sprintf("TKT-%d", time.Now().Unix()),
		Title:        req.GetTitle(),
		Description:  req.GetDescription(),
		Priority:     req.GetPriority(),
		Type:         "request", // default type
		Status:       "open",
	}

	if req.GetRelatedAlertId() != "" {
		ticket.AlertID = req.GetRelatedAlertId()
		ticket.Type = "alert"
	}

	if err := s.repo.CreateTicket(ctx, ticket); err != nil {
		return nil, err
	}

	return ticket, nil
}

func (s *ticketService) GetTicketStatistics(ctx context.Context) (*ticketv1.GetTicketStatisticsResponse, error) {
	// Get all tickets to calculate statistics
	tickets, total, err := s.repo.ListTickets(ctx, repository.Filter{
		Limit:  10000, // Get all tickets
		Offset: 0,
	})
	if err != nil {
		return nil, err
	}

	stats := &ticketv1.GetTicketStatisticsResponse{
		Total: int32(total),
	}

	// Count tickets by status
	for _, ticket := range tickets {
		switch ticket.Status {
		case "open":
			stats.Open++
		case "in_progress", "in-progress":
			stats.InProgress++
		case "resolved":
			stats.Resolved++
		case "closed":
			stats.Closed++
		}
	}

	return stats, nil
}

func (s *ticketService) UpdateTicket(ctx context.Context, req *ticketv1.UpdateTicketRequest) (*model.Ticket, error) {
	// Get existing ticket
	ticket, err := s.repo.GetTicket(ctx, req.GetId())
	if err != nil {
		return nil, err
	}
	if ticket == nil {
		return nil, fmt.Errorf("ticket not found: %s", req.GetId())
	}

	// Update fields if provided
	if req.GetStatus() != "" {
		ticket.Status = req.GetStatus()
	}
	if req.GetPriority() != "" {
		ticket.Priority = req.GetPriority()
	}
	if req.GetAssigneeId() != "" {
		ticket.AssignedTo = req.GetAssigneeId()
	}

	// Update in database
	if err := s.repo.UpdateTicket(ctx, ticket); err != nil {
		return nil, err
	}

	return ticket, nil
}

func (s *ticketService) AddComment(ctx context.Context, ticketID string, content string, userName string, isInternal bool) (*model.Ticket, error) {
	// Get existing ticket
	ticket, err := s.repo.GetTicket(ctx, ticketID)
	if err != nil {
		return nil, err
	}
	if ticket == nil {
		return nil, fmt.Errorf("ticket not found: %s", ticketID)
	}

	// Add comment to ticket
	comment := model.Comment{
		Content:    content,
		UserName:   userName,
		IsInternal: isInternal,
		CreatedAt:  time.Now(),
	}

	ticket.Comments = append(ticket.Comments, comment)

	// Update ticket in database
	if err := s.repo.UpdateTicket(ctx, ticket); err != nil {
		return nil, err
	}

	return ticket, nil
}

var _ Service = (*ticketService)(nil)
